#include "func.h"
#include "function.h"
#include "mysql.h"
#include "passwd.h"
#include "tcp.h"
#include "serverFunction.h"



#define WORKNUM 5
#define IPADDR "127.0.0.1"

extern const int USER = 100;
extern int clients_list[USER] ;
extern int clientSum ;
extern int exitPipe[2];


// 启动方式：./server ip 线程池大小
int main(int argc, char* argv[])
{
    //ARGS_CHECK(argc, 3);
    pipe(exitPipe);                     // exitPipe在function.c文件里声明,用来实现线程池退出
    if(fork() != 0)                     
    {
        close(exitPipe[0]);
        signal(SIGUSR1, sigFunc);
        wait(NULL);
        exit(0);
    }
    close(exitPipe[1]);                 
   // int workerNum = atoi(argv[2]);      // argv[2]是输入的线程池大小
    int workerNum = WORKNUM;      // argv[2]是输入的线程池大小        


      // 创建一个factory对象，
      //对象里包含：线程池tidArr;
     // 线程池大小threadNum;
      //任务队列taskQueue；
     // 运行标志runningFlag(用于线程池退出，该参数为0时表示已没有子线程正在运行，可以退出)
     factory_t factory; 

     // factory初始化,设置factory的线程池大小threadNum = workerNum
     factoryInit(&factory, workerNum);   //serverFunction.c

     // 给factory对象的线程池创建并初始化线程（上一步只整了个池子，这一步往池子里填线程）
      makeWorker(&factory);   //serverFUnction.c
                                                                      //int sockFd;

    pthreadpoll_t* pthreadpoll = (pthreadpoll_t*)calloc(MAXEVENTS,sizeof(pthreadpoll_t));
    pthreadpollinit(pthreadpoll);
    int arrfd[3]={-1};
    

    int sfd = 0;
   // tcpInit(argv[1], "8887", &sfd);//tcp.c
    tcpInit(IPADDR, "8887", &sfd);//serverFUNCTION.c
    int ret = 0;
    int epfd = epoll_create(1);         // 生成epoll例程
    ERROR_CHECK(epfd, -1, "create");
    struct epoll_event* peventlist = (struct epoll_event*)calloc(10,sizeof(struct epoll_event));
    struct epoll_event events[2];       // 事件数组
    addfd(epfd, sfd, true);     //serverFunction.c   
    // 初始化套接字的events数组,添加需要读取数据的情况EPOLLIN和边缘触发，设置为非阻塞状态
    addfd(epfd, exitPipe[0], true);     // 同上，把用于退出的管道设置为边缘触发

    events->data.fd=sfd;
    events->events=EPOLLIN;
    epoll_ctl(epfd,EPOLL_CTL_ADD,sfd, events);

    MYSQL * conn = mysqlInit();
    char buf[1024] = {0};
    memset(buf, 0, sizeof(buf));
    char username[64] = {0};
    char passwd[64] = {0};
    
    char filename[1024] = {0};
    char order[1024] = {0};
    memset(filename, 0, sizeof(filename));
    memset(order, 0, sizeof(order));
    int q = 0;

    while(1)
    {
        int readyNum = epoll_wait(epfd, events, 2, -1);     // 返回发生事件的文件描述符
        if(readyNum == 0)
            printf("epoll_wait timeout\n");
        else if(readyNum == -1 && errno == EINTR)
            continue;
        else if(readyNum < 0)
        {
            perror("epoll failure");
            break;
        }
        printf("epoll_events_count = %d\n", readyNum);      // 在server端打印文件描述符
        for(int i = 0; i < readyNum; ++i)
        {
            int sockfd = events[i].data.fd;
            if(sockfd == sfd)                               // 有新用户接入时
            {
                struct sockaddr_in client_address;
                socklen_t client_addrLength = sizeof(struct sockaddr_in);
                int clientfd = accept(sfd, (struct sockaddr*)&client_address, &client_addrLength);
                pthread_mutex_lock(&factory.taskQueue.mutex);           // 设置任务队列互斥, 防止往任务队列入队时其他用户也在入队导致冲突
                taskEnQueue(&factory.taskQueue, clientfd); //serverFunction.c      
                // 任务入队
                ret = recv(clientfd, buf, sizeof(buf), 0);
                    sysNote(passwd);
                char message[64];
                bzero(message, 64);
                if(strcmp(buf, "1") == 0)
                {
                    sprintf(message, "Please input username:");
                    send(clientfd, message, 64, 0);
                    ret = recv(clientfd, username, sizeof(username), 0);
                    sysNote(username);//passwd.c
                    ERROR_CHECK(ret, -1, "get username");
                  
                    bzero(message, 64);
                    sprintf(message, "Please input passwd:");
                    send(clientfd, message, 64, 0);
                    ret = recv(clientfd, passwd, sizeof(passwd), 0);
                    sysNote(passwd);
                    ERROR_CHECK(ret, -1, "get passwd");
                    
                    ret = passwdCheck(conn, username, passwd);//passwd.c
                    printf("after pwdcheck\n");

                 ////////////日志记录///////////////////////////////////////////////
                        time_t now = time(NULL);
                        struct tm *pTm = localtime(&now);
                        syslog(LOG_INFO,"%4d%2d%2d %2d:%2d:%2d user:%s  login成功！\n",
                        pTm->tm_year+1900,pTm->tm_mon+1, pTm->tm_mday,
                        pTm->tm_hour, pTm->tm_min, pTm->tm_sec,                                                                           
                        username);
          ////////////////////日志记录/////////////////////////
                    bzero(message, 64);
                    if(ret == -1) exit(0);
                    sprintf(message, "User Login success");
                    send(clientfd, message, 64, 0);
                }
                else if(strcmp(buf, "2") == 0)
                {
                    printf("register\n");
                    sprintf(message, "Please input username:");
                    send(clientfd, message, 64, 0);
                    ret = recv(clientfd, username, sizeof(username), 0);
                    sysNote(passwd);
                    ERROR_CHECK(ret, -1, "recv username");
                    
                    bzero(message, 64);
                    sprintf(message, "Please input passwd:");
                    send(clientfd, message, 64, 0);
                    recv(clientfd, passwd, sizeof(passwd), 0);
                    sysNote(passwd);
                   
                    AddUserToMysql(conn, username, passwd);  //passwd.c


                     ////////////日志记录///////////////////////////////////////////////
                        time_t now = time(NULL);
                        struct tm *pTm = localtime(&now);
                        syslog(LOG_INFO,"%4d%2d%2d %2d:%2d:%2d user:%s  注册成功！\n",
                        pTm->tm_year+1900,pTm->tm_mon+1, pTm->tm_mday,
                        pTm->tm_hour, pTm->tm_min, pTm->tm_sec,
                        username);
          ////////////////////日志记录/////////////////////////

          
                    bzero(message, 64);
                }
		
		int flags = fcntl(clientfd, F_GETFL, 0);
                flags |= O_NONBLOCK;
                fcntl(clientfd, F_GETFL, flags);
		struct epoll_event evt;
                evt.data.fd = clientfd;
                evt.events = EPOLLIN;
                epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &evt);

		printf("client connection from:%s : %d, clientfd = %d\n", inet_ntoa(client_address.sin_addr), ntohs(client_address.sin_port), clientfd);
                addfd(epfd, clientfd, true);
                clientSum++;                                            // 现有用户数+1
                clients_list[clientSum - 1] = clientfd;                 // 将文件描述符添加到现有用户数组
                printf("Add new clientfd = %d to epoll\n", clientfd);
                bzero(message, 64);
                sprintf(message, "Hello");
                int ret = send(clientfd, message, 64, 0);               // 给用户发送hello表示成功接入
                ERROR_CHECK(ret, -1, "send");
                pthread_cond_signal(&factory.taskQueue.cond);           
                pthread_mutex_unlock(&factory.taskQueue.mutex);         // 解锁任务队列互斥
            }
            else if(sockfd == exitPipe[0])                              // 退出线程池时
            {
                puts("exit threadPool!");
                factory.runningFlag = 0;
                pthread_cond_broadcast(&factory.taskQueue.cond);
                for(int j = 0;j < workerNum; ++j)
                    pthread_join(factory.tidArr[j], NULL);
                puts("done");
                exit(0);
            }
            else        // 发生事件的用户不是新接入的用户
            {
                pthread_mutex_lock(&factory.taskQueue.mutex);   //设置互斥，一个用户通信时其他用户连接着但不能通信
                int ret = sendBroadcastmessage(sockfd,epfd, conn, username);         // 执行服务器端和客户端的通信
                ERROR_CHECK(ret, -1, "Broadcast");
                pthread_mutex_unlock(&factory.taskQueue.mutex);
            }
        }
    }
    close(sfd);
    close(epfd);
    return 0;
}

#undef WORKNUM 
#undef IPADDR
#undef MAXEVENTS
